﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Threading;

namespace AddAudio2Video
{
    public partial class mainFrom : Form
    {
        static object locker  = new object();
        static object locker_allFile = new object();
        static object locker_listView = new object();
        static object locker_thread = new object();
        private Dictionary<string, CustomFile> allFiles = new Dictionary<string, CustomFile>();
        private string ffmpeg = "";
        private string tmpPath = "";
        private string adAudioPath = "";
        private string newVideoPath = "";
        private double AudioTimeLength = 0.0;
        private int threadNum = 2;
        private bool isRunning = false;
        private double beginPos = 0;
        private double endPos = 0;

        private string null60Audio;
        public mainFrom()
        {
            InitializeComponent();
            this.listView_result.Columns.Add("编号", 50, HorizontalAlignment.Left);
            this.listView_result.Columns.Add("文件名称", 200, HorizontalAlignment.Left);
            //this.listView_result.Columns.Add("上传周次", 120, HorizontalAlignment.Left);
            this.listView_result.Columns.Add("状态", 210, HorizontalAlignment.Left);

            this.listView_result.View = System.Windows.Forms.View.Details;
            this.listView_result.CheckBoxes = true;
            this.listView_result.AllowDrop = true;

            this.null60Audio = System.Windows.Forms.Application.StartupPath + "\\data\\null60.mp3";
        }

        private void mainFrom_Load(object sender, EventArgs e)
        {
            this.ffmpeg =  System.Windows.Forms.Application.StartupPath + "\\bin\\ffmpeg.exe";
            this.tmpPath = System.Windows.Forms.Application.StartupPath + "\\temp\\";


            Image ewm_img = Resource1.ewm;
            Image fix_img = new Bitmap(ewm_img, 148, 148);
            this.pictureBox1.Image = fix_img;
            this.SetText("~~扫一扫加作者QQ：5068896，送靠谱营销软件！~~\n");
            this.SetText("使用方法：");
            this.SetText("\t1.设置广告音频存放的位置");
            this.SetText("\t2.设置编辑后的视频存放位置");
            this.SetText("\t3.把要编辑的视频拖拽到文件列表中");
            this.SetText("\t4.点击开始");
            //this.SetText("(PS:程序存放路径中有空格，导致无法正常启动。)");

            this.tb_pos1.Text = "20";
            this.tb_pos2.Text = "40";
            //if(!Directory.Exists(this.tmpPath))
            //{
            //    Directory.CreateDirectory(this.tmpPath) ;
            //}
        }
        private void SetText(string text)
        {
            try
            {
                this.Invoke((EventHandler)delegate
                {
                    lock (locker)
                    {
                        this.tb_status.AppendText(text + "\r\n");
                    }
                });
            }
            catch
            { }
        }

        private void listView_result_DragEnter(object sender, DragEventArgs e)
        {
            //this.SetText("DragEnter");
            if (this.isRunning)
            {
                MessageBox.Show("请先停止，在添加文件");
                return;
            }
            try
            {
                e.Effect = DragDropEffects.Copy;
                String[] str_Drop = (String[])e.Data.GetData(DataFormats.FileDrop, true);//必须用字符串数组
                foreach (string fileName in str_Drop)
                {
                    //this.SetText(fileName);
                    this.SetText(string.Format("视频文件<<{0}>>加载成功",Path.GetFileName(fileName)));
                    this.importFile(fileName);
                }
                //string fileName = str_Drop[0];
                //int count = importCookie(fileName);
                //MessageBox.Show("DRAGENTER:" + fileName);
            }
            catch//Exception ex)
            {
                //MessageBox.Show("DRAGENTER:" + ex.Message);
            }
        }
        private bool importFile(string fileName)
        {
            try
            {
                if (!File.Exists(fileName))
                {
                    //MessageBox.Show("文件:{0}不存在");
                    this.SetText(string.Format("文件:{0}不存在",fileName));
                    return false;
                }
                //this.SetText(string.Format("添加{}到列表中",fileName));
                CustomFile tpfile = new CustomFile();
                tpfile.Name = Path.GetFileName(fileName);
                tpfile.Path = fileName;
                tpfile.Status = 0;
                
                string key = this.getMD5(tpfile.Name);
                bool needUpload = false;
                lock (locker_allFile)
                {
                    if (!this.allFiles.ContainsKey(key))
                    {
                        allFiles.Add(key, tpfile);
                        this.updateListView(tpfile);
                        needUpload = true;
                    }
                    else
                    {//文件名已经存在，是替换？
                        this.SetText("文件：" + tpfile.Name + "已上传，如需重传，请先右击删除");
                        //删除文件的时候，要判断是不是已经上传到课件系统里，如果已经上传，要删除
                        //上传到代理服务器的可以覆盖
                        //修改上传时间的，要在代理服务器上删除，然后重新上传
                    }
                }
                if (needUpload)
                {
                    
                }

                return true;
            }
            catch //(Exception ex)
            {
                //MessageBox.Show("IMPORTFILE:" + ex.Message);
                return false;
            }
        }
        private void updateListView(CustomFile tpfile)
        {
            int count = this.listView_result.Items.Count;
            ListViewItem lvi = new ListViewItem();
            lvi.Checked = true;
            lvi.Text = (count + 1).ToString();

            string key = this.getMD5(tpfile.Name);

            if (this.allFiles.ContainsKey(key))
            {
                lvi.SubItems.Add(tpfile.Name);
                //lvi.SubItems.Add(tpfile.Date);
                lvi.SubItems.Add(this.getCustomFileStatusStr(tpfile.Status));
                //this.listView_result.Items.Add(lvi);//进行查找更新，不能直接添加???
                this.SetListView(lvi);
            }
            else
            {
                this.SetText("数据不一致...");
            }
        }
        private string getCustomFileStatusStr(int status)
        {
            string statusStr = "";
            switch (status)
            {
                case 0:
                    statusStr = "未开始";//"未上传";
                    break;
                case 1:
                    statusStr = "正在植入广告...";
                    break;
                case 2:
                    statusStr = "广告植入成功...";
                    break;
                case 3:
                    statusStr = "广告植入失败...";
                    break;
                default:
                    statusStr = "程序梦游中...";
                    break;
            }
            return statusStr;
        }
        private void SetListView(ListViewItem lvi, bool rewrite = true)
        {
            try
            {
                this.Invoke((EventHandler)delegate
                {
                    lock (locker_listView)
                    {
                        ListViewItem lvi_tmp = this.listView_result.FindItemWithText(lvi.SubItems[1].Text);

                        this.listView_result.BeginUpdate();
                        if (lvi_tmp != null)
                        {
                            lvi_tmp.SubItems[2].Text = lvi.SubItems[2].Text;
                            //lvi_tmp.SubItems[3].Text = lvi.SubItems[3].Text;
                        }
                        else
                        {
                            lvi.Checked = true;
                            this.listView_result.Items.Add(lvi);//进行查找更新，不能直接添加
                        }

                        this.listView_result.EndUpdate();

                    }
                });
            }
            catch //(Exception ex)
            {
                //MessageBox.Show("SETLISTVIEW：" + ex.Message);
            }
        }
        private string getMD5(string str)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] fromData = System.Text.Encoding.Unicode.GetBytes(str);
            byte[] targetData = md5.ComputeHash(fromData);
            string byte2String = null;

            for (int i = 0; i < targetData.Length; i++)
            {
                byte2String += targetData[i].ToString("x");
            }

            return byte2String;
        }

        private void btn_importAudio_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.InitialDirectory = "C:\\Users\\Administrator\\Desktop";
            ofd.Filter = "All files (*.*)|*.*";
            ofd.FilterIndex = 2;
            ofd.RestoreDirectory = true;
            string fileName = "";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                fileName = ofd.FileName;
                this.tb_audioPath.Text = fileName;
            }
    
        }

        private void btn_begin_Click(object sender, EventArgs e)
        {
            //this.AudioTimeLength = AudioVideoTools.getAVTimeLength(this.ffmpeg,this.tb_audioPath.Text);
            this.adAudioPath = this.tb_audioPath.Text.Trim();
            if (this.adAudioPath.Length == 0)
            {
                MessageBox.Show("请先设置广告录音的位置...");
                return;
            }

            if (!File.Exists(this.adAudioPath))
            {
                MessageBox.Show("请检查广告录音是否存在...");
                return;
            }
            this.newVideoPath = this.tb_retPath.Text.Trim();
            if (this.newVideoPath.Length == 0)
            {
                MessageBox.Show("请先设置结果的存放位置...");
                return;
            }
            if (!Directory.Exists(this.newVideoPath))
            {
                MessageBox.Show("请检查结果存放目录是否存在...");
                return;
            }
            int pos = 0;
            if (int.TryParse(this.tb_pos1.Text.Trim(), out pos))
            {
                this.beginPos = pos/100.0;
            }
            else
            {
                MessageBox.Show("请检查视频插入的开始位置...");
                return;
            }

            if (int.TryParse(this.tb_pos2.Text.Trim(), out pos))
            {
                this.endPos = pos/100.0;
                if (this.endPos <= this.beginPos)
                {
                    this.endPos = this.beginPos + 0.1;
                    this.endPos = Math.Min(this.endPos, 1.0);
                }
            }
            else
            {
                MessageBox.Show("请检查视频插入的结束位置...");
                return;
            }
            lock (locker_allFile)
            {
                if (allFiles.Count == 0)
                {
                    MessageBox.Show("请把需要编辑的视频拖放到文件列表中...");
                    return;
                }
            }
            lock (locker_thread)
            {
                if (this.isRunning)
                {
                    MessageBox.Show("我已经在干活了，你想作甚...");
                    return;
                }
                this.isRunning = true;
            }

            try
            {
                Thread t = new Thread(work);
                t.Start();
                return ;
            }
            catch (Exception ex)
            {
                this.SetText("Exception: " + ex.Message);
                return ;
            }

        }

        private void work()
        {
            lock (locker_allFile)
            {
                while (this.isRunning)
                {
                    foreach (string key in allFiles.Keys)
                    {
                        if (!this.isRunning)
                        {
                            return;
                        }
                        CustomFile cf = allFiles[key];
                        if (cf.Status > 0)     
                        {
                            if (this.isRunning)
                            {
                                continue;
                            }
                            else 
                            {
                                return;
                            }
                        }
                        cf.Status = 1 ;
                        allFiles[key].Status = 1;
                        updateListView(cf);
                        int ret = 0;
                        try
                        {
                            ret = _work(this.adAudioPath, cf.Path, this.newVideoPath + "\\" + cf.Name) ;
                        }
                        catch (Exception ex)
                        {
                            ret = -1;
                            this.SetText("**@@##@@**"+ex.Message);
                        }
                        if (ret == 0)
                        {
                            cf.Status = 2;
                        }
                        else
                        {
                            cf.Status = 3;
                        }
                        allFiles[key].Status = cf.Status;
                        updateListView(cf);
                        
                    }
                    lock (locker_thread)
                    {
                        this.isRunning = false;
                    }
                }
                
            }
        }
        private int _work(string audio, string video, string to)
        {
            this.SetText(string.Format("开始编辑视频<<{0}>> ...", Path.GetFileName(video)));
            string tempPath = this.tmpPath + "\\" + DateTime.Now.GetHashCode() + "\\";
            if (!Directory.Exists(tempPath))
            {
                Directory.CreateDirectory(tempPath);
            }

            //获取提取的视频时间长度
            double exTime = AudioVideoTools.getAVTimeLength(ffmpeg, video);

            this.SetText("计算视频播放时长...");
            this.SetText(string.Format("视频 {0} 长度为：{1}秒", Path.GetFileName(video), exTime));
            if (exTime < 0)
            {
                this.SetText("视频长度读取错误，植入广告失败...");
                return -1;
            }
            
            int beginAdTime = getAdBeginTime(exTime);
            //string ext = Path.GetExtension(video); 
            this.SetText(string.Format("广告在视频的第{0}秒", beginAdTime));
            string newAudio = tempPath + "a2.mp3";
            AudioVideoTools.genNewAdAudio4Video(ffmpeg, audio, null60Audio, beginAdTime, newAudio, tempPath);
            AudioVideoTools.genNewVideo(ffmpeg, newAudio, video, to, rb_h264.Checked);
            this.SetText("\n");
            if (!Directory.Exists(tempPath))
            {
                Directory.Delete(tempPath);
            }
            return 0;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="audio"></param>
        /// <param name="video"></param>
        /// <param name="to"></param>
        private int _work3(string audio, string video, string to)
        {
            this.SetText(string.Format("开始编辑视频<<{0}>> ...", Path.GetFileName(video)));
            string tempPath = this.tmpPath + "\\" +DateTime.Now.GetHashCode()+"\\";
            if(!Directory.Exists(tempPath))
            {
                Directory.CreateDirectory(tempPath) ;
            }
            //提取音频from 视频
            //string ext = Path.GetExtension(video);
            string audioFormat = AudioVideoTools.getAudioFormat(ffmpeg,video);
            string exAudioPath = tempPath + "exAudio." + audioFormat ;// + ext;
            this.SetText(string.Format("从视频<<{0}>>中提取音频文件...",Path.GetFileName(video)));
            AudioVideoTools.extractAudioFromVideo(ffmpeg, video, exAudioPath);
            //音频转码
            string newAudio = this.tmpPath + "\\newAdAudio." + audioFormat;
            if (!File.Exists(newAudio))
            {
                AudioVideoTools.enCodeAudio(ffmpeg, audio, newAudio);
            }
            //获取提取的视频时间长度
            double exTime = AudioVideoTools.getAVTimeLength(ffmpeg, video);

            this.SetText("计算视频播放时长...");
            this.SetText(string.Format("视频 {0} 长度为：{1}秒", Path.GetFileName(video), exTime));
            if (exTime < 0)
            {
                this.SetText("视频长度读取错误，植入广告失败...");
                return -1;
            }
            //获取广告音频长度
            double adTime = AudioVideoTools.getAVTimeLength(ffmpeg, audio) ;
            //this.SetText(string.Format("获取广告音频<<{0}>>长度...", Path.GetFileName(audio)));
            this.SetText(string.Format("广告 {0} 长度为：{1}秒", Path.GetFileName(audio), adTime));
            if (exTime < 0)
            {
                this.SetText("广告音频长度读取错误，植入广告失败...");
                return -1;
            }
            double beginAdTime = (double)getAdBeginTime(exTime);
            double endAdTime = beginAdTime + adTime;

            //切割提取的音频成3段
            string exAudio1 = tempPath + "exAudio1." + audioFormat ;// + ext ;
            string exAduio2 = tempPath + "exAudio2." + audioFormat ; // + ext ;
            string exAudio3 = tempPath + "exAudio3." + audioFormat ; // + ext ;
            this.SetText(string.Format("把从<<{0}>>中提取的音频成3段...", Path.GetFileName(video)));
            AudioVideoTools.cutAudio2Part(ffmpeg, exAudioPath, exAudio1, 0, beginAdTime);
            //AudioVideoTools.cutAudio2Part(ffmpeg, exAudioPath, exAduio2, beginAdTime, exTime-beginAdTime);
            //AudioVideoTools.cutAudio2Part(ffmpeg, exAudioPath, exAudio3, beginAdTime + adTime, exTime - endAdTime);
            AudioVideoTools.cutAudio2Part(ffmpeg, exAudioPath, exAudio3, beginAdTime + adTime, exTime);

            //合并新的音频
            string [] files = {exAudio1,newAudio,exAudio3} ;
            string targetAudio = tempPath + "newAudio.mp3"; //+audioFormat;// + ext ;
            string fromFile = tempPath + "list.txt" ;
            this.SetText("音频文件转码合并中...");
            AudioVideoTools.combineAudios21(ffmpeg, files, fromFile, targetAudio);

            //合成新的视频
            this.SetText("植入广告音频...");
            AudioVideoTools.addAudio2Video(ffmpeg, targetAudio, video, to);
            this.SetText(string.Format("视频文件<<{0}>>成功植入广告", Path.GetFileName(video)));
            this.SetText(string.Format("广告在视频的第{0}秒",beginAdTime));
            if (Directory.Exists(tempPath))
            {
                Directory.Delete(tempPath, true);
            }
            this.SetText("\n");
            return 0;
        }
        private int getAdBeginTime(double second)
        {
            int time = 0 ;
            Random rand = new Random(DateTime.Now.GetHashCode()) ;
            int b = (int)(second * this.beginPos) ;
            int e = (int)(second * this.endPos) ;
            //int b = (int)(second * 0.2);
            //int e = (int)(second * 0.3);
            time = rand.Next(b, e);
            return time ;
        }

        private void btn_stop_Click(object sender, EventArgs e)
        {
            lock(locker_thread)
            {
                if (this.isRunning)
                {
                    this.SetText("有视频正在编辑，完成后立即停止...");
                }
                else 
                {
                    this.SetText("已停止...");
                }
                this.isRunning = false;
            }
        }

        private void btn_setRetPath_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            fbd.Description = "请选择文件路径";
            if (fbd.ShowDialog() == DialogResult.OK)
            {
                string foldPath = fbd.SelectedPath;
                this.tb_retPath.Text = foldPath;
                //MessageBox.Show("已选择文件夹:" + foldPath, "选择文件夹提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string fromAudio = this.tb_audioPath.Text.Trim();
            string toAudio = "";
            string path = "C:\\tmp\\";
            foreach (string key in allFiles.Keys)
            {
                string fromVideo = allFiles[key].Path;
                string format = AudioVideoTools.getAudioFormat(ffmpeg, fromVideo);
                toAudio = path + Path.GetFileNameWithoutExtension(allFiles[key].Name) + "." + format;
                AudioVideoTools.extractAudioFromVideo(ffmpeg, fromVideo, toAudio);
            }
        }


    }
}
